﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace sXNAke
{
    abstract class AnimatedGameEntity: GameEntity, IGameEntity
    {

        #region animation
            /// <summary>
            /// The animations for this sprite
            /// </summary>
            private List<Animation> animations;

            ///<summary>
            /// The current animation which will be played
            /// </summary>
            private Animation currentAnimation;

            /// <summary>
            /// the current frame
            /// </summary>
            private Point currentFrame;

            /// <summary>
            /// Time of last frame
            /// </summary>
            private float lastFrametick;

            /// <summary>
            /// The source rectangle
            /// </summary>
            private Rectangle sourceRectangle;
        #endregion

        /// <summary>
        /// the effects for this sprites
        /// </summary>
        private SpriteEffects spriteEffects;
        
        private Vector2 velocity;
        public Vector2 Velocity
        {
            get { return velocity; }
            set { velocity = value; }
        }

        public new Rectangle CollisionRect
        {
            get
            {
                return new Rectangle((int)position.X, (int)position.Y, (int)currentAnimation.getFrameSize().X, (int)currentAnimation.getFrameSize().Y);
            }
        }


        public AnimatedGameEntity(Texture2D image, Vector2 position)
            : base(image, position)
        {
            this.animations = new List<Animation>();

            this.spriteEffects = SpriteEffects.None;
            this.lastFrametick = 0;
            this.currentFrame.X = 0;
            this.currentFrame.Y = 0;
        }

      
        public void deFlipHorizontally()
        {
            this.spriteEffects = SpriteEffects.None;
        }

        /// <summary>
        /// Flips the image in horizontal. If the sprite is fliped, him returns to original state
        /// </summary>
        public void flipHorizontally()
        {
            this.spriteEffects = SpriteEffects.FlipHorizontally;            
        }

        /// <summary>
        /// Flips the image in vertical. If the sprite is fliped, him returns to original state
        /// </summary>
        public void flipVertically()
        {
            if (spriteEffects == SpriteEffects.None)
            {
                spriteEffects = SpriteEffects.FlipVertically;
            }
        }
        
        /// <summary>
        /// Adds an animation
        /// </summary>
        /// <param name="animation">An animation</param>
        public void addAnimation(Animation animation)
        {
            if (!Util.isNull(animation)) // do not add null animation
            {
                animations.Add(animation);
            }
        }

        /// <summary>
        /// Removes an animation
        /// </summary>
        /// <param name="animation">An animation</param>
        public void removeAnimation(Animation animation)
        {
            if (!Util.isNull(animation)) // do not add null animation
            {
                animations.Remove(animation);
            }
        }

        /// <summary>
        /// Load and set as currentAnimation
        /// </summary>
        /// <param name="name">An animation name</param>
        private void loadAnimation(string name)
        {
            if (currentAnimation != null)
            {
                if (currentAnimation.getName() == name)
                {
                    return;
                }
            }

            currentAnimation = searchAnimation(name);

            if (currentAnimation != null)
            {
                currentFrame.X = currentAnimation.getFrameX();
                currentFrame.Y = currentAnimation.getFrameY();
                lastFrametick = 0;
                sourceRectangle = new Rectangle(0, 0, (int)currentAnimation.getFrameSize().X, (int)currentAnimation.getFrameSize().Y);
            }
        }

        /// <summary>
        /// Search an animation
        /// </summary>
        /// <param name="name">An animation name</param>
        /// <returns>An animation</returns>
        private Animation searchAnimation(string name)
        {
            return animations.Find(delegate(Animation a)
            {
                return a.getName() == name;
            });
        }

        /// <summary>
        /// Check if an animation finished.
        /// </summary>
        /// <param name="name">An animation name</param>
        /// <returns>True, if animation over.\nIf the animation name is not the name of the current animation or animation not finished, returns false</returns>
        public bool isAnimationFinished(string name)
        {
            Animation animation = searchAnimation(name);

            if (animation.getName() == currentAnimation.getName())
            {
                if (currentAnimation.getAxis() == Axis.X)
                {
                    return currentFrame.X >= currentAnimation.getTotalFrames() - 1;
                }
                else
                {
                    return currentFrame.Y >= currentAnimation.getTotalFrames() - 1;
                }
            }

            return false;
        }

        /// <summary>
        /// Set the currentAnimation. That animation must be inside the list of animations
        /// </summary>
        /// <param name="name">A name of an animation</param>
        protected void setCurrentAnimation(string name)
        {
            loadAnimation(name);
        }

        /// <summary>
        /// Plays an animation
        /// </summary>
        /// <param name="name">The animation name for play</param>
        public void playCurrentAnimation(string name, GameTime gameTime)
        {
            if (gameTime.TotalGameTime.TotalMilliseconds >= lastFrametick + currentAnimation.getFrameTime())
            {
                lastFrametick = (float)gameTime.TotalGameTime.TotalMilliseconds;

                sourceRectangle.X = (int)(currentFrame.X * currentAnimation.getFrameSize().X);
                sourceRectangle.Y = (int)(currentFrame.Y * currentAnimation.getFrameSize().Y);

                // if there is not more than 1 frame, do not make animation
                if (currentAnimation.getTotalFrames() <= 1)
                    return;

                if (currentAnimation.getAxis() == Axis.X)
                {
                    if (currentFrame.X >= currentAnimation.getTotalFrames() - 1)
                        currentFrame.X = currentAnimation.getFrameX();
                    else currentFrame.X++;
                }
                else
                {
                    if (currentFrame.Y >= currentAnimation.getTotalFrames() - 1)
                        currentFrame.Y = currentAnimation.getFrameY();
                    else currentFrame.Y++;
                }
            }
        }

        public override void update(GameTime gameTime)
        {
            if (!Util.isNull(currentAnimation))
            {
                playCurrentAnimation(currentAnimation.getName(), gameTime);
            
            }
            else
            {
                //throw new Exception("the animation is null");
            }
        }

        public override void draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(this.getImage(), this.position, sourceRectangle, Color.White, 0.0f, Vector2.Zero,1, spriteEffects, 1);
        }      
    }
}
